#ifndef __CRYTHREADIMPL_WINDOWS_H__
#define __CRYTHREADIMPL_WINDOWS_H__
#pragma once

//#include <IThreadTask.h>

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#ifndef XENON
#include <windows.h>
#endif
#include <process.h>

struct SThreadNameDesc
{
	DWORD dwType;
	LPCSTR szName;
	DWORD dwThreadID;
	DWORD dwFlags;
};

THREADLOCAL CrySimpleThreadSelf* CrySimpleThreadSelf::m_Self = NULL;

//////////////////////////////////////////////////////////////////////////
CryEvent::CryEvent()
{
	m_handle = (void*)CreateEvent(NULL, FALSE, FALSE, NULL);
}

//////////////////////////////////////////////////////////////////////////
CryEvent::~CryEvent()
{
	CloseHandle(m_handle);
}

//////////////////////////////////////////////////////////////////////////
void CryEvent::Reset()
{
	ResetEvent(m_handle);
}

//////////////////////////////////////////////////////////////////////////
void CryEvent::Set()
{
	SetEvent(m_handle);
}

//////////////////////////////////////////////////////////////////////////
void CryEvent::Wait() const
{
	WaitForSingleObject(m_handle, INFINITE);
}

//////////////////////////////////////////////////////////////////////////
bool CryEvent::Wait( const uint32 timeoutMillis ) const
{
	if (WaitForSingleObject(m_handle, timeoutMillis) == WAIT_TIMEOUT)
		return false;
	return true;
}

//////////////////////////////////////////////////////////////////////////
// CryLock_WinMutex
//////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////
CryLock_WinMutex::CryLock_WinMutex() : m_hdl(CreateMutex(NULL, FALSE, NULL)) {}
CryLock_WinMutex::~CryLock_WinMutex()
{
	CloseHandle(m_hdl);
}

//////////////////////////////////////////////////////////////////////////
void CryLock_WinMutex::Lock()
{
	WaitForSingleObject(m_hdl, INFINITE);
}

//////////////////////////////////////////////////////////////////////////
void CryLock_WinMutex::Unlock()
{
	ReleaseMutex(m_hdl);
}

//////////////////////////////////////////////////////////////////////////
bool CryLock_WinMutex::TryLock()
{
	return WaitForSingleObject(m_hdl, 0) != WAIT_TIMEOUT;
}

//////////////////////////////////////////////////////////////////////////
// CryLock_CritSection
//////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////
CryLock_CritSection::CryLock_CritSection()
{
	InitializeCriticalSection((CRITICAL_SECTION*)&m_cs);
}

//////////////////////////////////////////////////////////////////////////
CryLock_CritSection::~CryLock_CritSection()
{
	DeleteCriticalSection((CRITICAL_SECTION*)&m_cs);
}

//////////////////////////////////////////////////////////////////////////
void CryLock_CritSection::Lock()
{
	EnterCriticalSection((CRITICAL_SECTION*)&m_cs);
}

//////////////////////////////////////////////////////////////////////////
void CryLock_CritSection::Unlock()
{
	LeaveCriticalSection((CRITICAL_SECTION*)&m_cs);
}

//////////////////////////////////////////////////////////////////////////
bool CryLock_CritSection::TryLock()
{
	return TryEnterCriticalSection((CRITICAL_SECTION*)&m_cs) != FALSE;
}

//////////////////////////////////////////////////////////////////////////
// most of this is taken from http://www.cs.wustl.edu/~schmidt/win32-cv-1.html
//////////////////////////////////////////////////////////////////////////
CryConditionVariable::CryConditionVariable()
{
	m_waitersCount = 0;
	m_wasBroadcast = 0;
	m_sema = CreateSemaphore(NULL, 0, 0x7fffffff, NULL);
	InitializeCriticalSection((CRITICAL_SECTION*)&m_waitersCountLock);
	m_waitersDone = CreateEvent(NULL, FALSE, FALSE, NULL);
}

//////////////////////////////////////////////////////////////////////////
CryConditionVariable::~CryConditionVariable()
{
	CloseHandle(m_sema);
	DeleteCriticalSection((CRITICAL_SECTION*)&m_waitersCountLock);
	CloseHandle(m_waitersDone);
}

//////////////////////////////////////////////////////////////////////////
void CryConditionVariable::Wait( LockType& lock )
{
	EnterCriticalSection( (CRITICAL_SECTION*)&m_waitersCountLock );
	m_waitersCount ++;
	LeaveCriticalSection( (CRITICAL_SECTION*)&m_waitersCountLock );

	SignalObjectAndWait( lock._get_win32_handle(), m_sema, INFINITE, FALSE );

	EnterCriticalSection( (CRITICAL_SECTION*)&m_waitersCountLock );
	m_waitersCount --;
	bool lastWaiter = m_wasBroadcast && m_waitersCount == 0;
	LeaveCriticalSection( (CRITICAL_SECTION*)&m_waitersCountLock );

	if (lastWaiter)
		SignalObjectAndWait( m_waitersDone, lock._get_win32_handle(), INFINITE, FALSE );
	else
		WaitForSingleObject( lock._get_win32_handle(), INFINITE );
}

//////////////////////////////////////////////////////////////////////////
bool CryConditionVariable::TimedWait( LockType& lock, uint32 millis )
{
	EnterCriticalSection( (CRITICAL_SECTION*)&m_waitersCountLock );
	m_waitersCount ++;
	LeaveCriticalSection( (CRITICAL_SECTION*)&m_waitersCountLock );

	bool ok = true;
	if (WAIT_TIMEOUT == SignalObjectAndWait( lock._get_win32_handle(), m_sema, millis, FALSE ))
		ok = false;

	EnterCriticalSection( (CRITICAL_SECTION*)&m_waitersCountLock );
	m_waitersCount --;
	bool lastWaiter = m_wasBroadcast && m_waitersCount == 0;
	LeaveCriticalSection( (CRITICAL_SECTION*)&m_waitersCountLock );

	if (lastWaiter)
		SignalObjectAndWait( m_waitersDone, lock._get_win32_handle(), INFINITE, FALSE );
	else
		WaitForSingleObject( lock._get_win32_handle(), INFINITE );

	return ok;
}

//////////////////////////////////////////////////////////////////////////
void CryConditionVariable::NotifySingle()
{
	EnterCriticalSection((CRITICAL_SECTION*)&m_waitersCountLock);
	bool haveWaiters = m_waitersCount > 0;
	LeaveCriticalSection((CRITICAL_SECTION*)&m_waitersCountLock);
	if (haveWaiters)
		ReleaseSemaphore(m_sema, 1, 0);
}

//////////////////////////////////////////////////////////////////////////
void CryConditionVariable::Notify()
{
	EnterCriticalSection( (CRITICAL_SECTION*)&m_waitersCountLock );
	bool haveWaiters = false;
	if (m_waitersCount > 0)
	{
		m_wasBroadcast = 1;
		haveWaiters = true;
	}
	if (haveWaiters)
	{
		ReleaseSemaphore( m_sema, m_waitersCount, 0 );
		LeaveCriticalSection( (CRITICAL_SECTION*)&m_waitersCountLock );
		WaitForSingleObject( m_waitersDone, INFINITE );
		m_wasBroadcast = 0;
	}
	else
	{
		LeaveCriticalSection( (CRITICAL_SECTION*)&m_waitersCountLock );
	}
}

//////////////////////////////////////////////////////////////////////////
CrySemaphore::CrySemaphore(int nMaximumCount, int nInitialCount)
{
	m_Semaphore = (void*)CreateSemaphore(NULL,nInitialCount,nMaximumCount, NULL);
}

//////////////////////////////////////////////////////////////////////////
CrySemaphore::~CrySemaphore()
{
	CloseHandle((HANDLE)m_Semaphore);
}

//////////////////////////////////////////////////////////////////////////
void CrySemaphore::Acquire()
{
	WaitForSingleObject((HANDLE)m_Semaphore,INFINITE);
}

//////////////////////////////////////////////////////////////////////////
void CrySemaphore::Release()
{
		ReleaseSemaphore((HANDLE)m_Semaphore,1,NULL);
}

//////////////////////////////////////////////////////////////////////////
CryFastSemaphore::CryFastSemaphore(int nMaximumCount, int nInitialCount) :
	m_Semaphore(nMaximumCount),
	m_nCounter(nInitialCount)
{
}

//////////////////////////////////////////////////////////////////////////
CryFastSemaphore::~CryFastSemaphore()
{	
}

//////////////////////////////////////////////////////////////////////////
void CryFastSemaphore::Acquire()
{
	int nCount = ~0;
	do
	{
		nCount = *const_cast<volatile int*>(&m_nCounter);
	}while( CryInterlockedCompareExchange( alias_cast<volatile LONG*>(&m_nCounter), nCount - 1, nCount) != nCount );
	
	// if the count would have been 0 or below, go to kernel semaphore
	if( (nCount - 1)  < 0 )
		m_Semaphore.Acquire();
}

//////////////////////////////////////////////////////////////////////////
void CryFastSemaphore::Release()
{
	int nCount = ~0;
	do
	{
		nCount = *const_cast<volatile int*>(&m_nCounter);
	}while( CryInterlockedCompareExchange( alias_cast<volatile LONG*>(&m_nCounter), nCount + 1, nCount) != nCount );
	
	// wake up kernel semaphore if we have waiter
	if( nCount < 0 )
		m_Semaphore.Release();
}

//////////////////////////////////////////////////////////////////////////
#if !defined(XENON)
CryRWLock::CryRWLock()
{
	STATIC_ASSERT(sizeof(m_Lock) == sizeof(PSRWLOCK), "RWLock-pointer has invalid size");
	InitializeSRWLock(reinterpret_cast<PSRWLOCK>(&m_Lock));
}

//////////////////////////////////////////////////////////////////////////
CryRWLock::~CryRWLock()
{
}

//////////////////////////////////////////////////////////////////////////
void CryRWLock::RLock() 
{
	AcquireSRWLockShared(reinterpret_cast<PSRWLOCK>(&m_Lock));
}

//////////////////////////////////////////////////////////////////////////
bool CryRWLock::TryRLock() 
{
	return TryAcquireSRWLockShared(reinterpret_cast<PSRWLOCK>(&m_Lock)) != 0; 
}

//////////////////////////////////////////////////////////////////////////
void CryRWLock::RUnlock() 
{
	ReleaseSRWLockShared(reinterpret_cast<PSRWLOCK>(&m_Lock));
}

//////////////////////////////////////////////////////////////////////////
void CryRWLock::WLock() 
{
	AcquireSRWLockExclusive(reinterpret_cast<PSRWLOCK>(&m_Lock));
}

//////////////////////////////////////////////////////////////////////////
bool CryRWLock::TryWLock() 
{
	return TryAcquireSRWLockExclusive(reinterpret_cast<PSRWLOCK>(&m_Lock)) != 0; 
}

//////////////////////////////////////////////////////////////////////////
void CryRWLock::WUnlock() 
{
	ReleaseSRWLockExclusive(reinterpret_cast<PSRWLOCK>(&m_Lock));
}

//////////////////////////////////////////////////////////////////////////
void CryRWLock::Lock() 
{
	WLock();
}

//////////////////////////////////////////////////////////////////////////
bool CryRWLock::TryLock() 
{
	return TryWLock(); 
}

//////////////////////////////////////////////////////////////////////////
void CryRWLock::Unlock()
{
	WUnlock();
}
#endif

//////////////////////////////////////////////////////////////////////////
CrySimpleThreadSelf::CrySimpleThreadSelf()
	: m_thread(NULL)
	, m_threadId(0)
{
}

//////////////////////////////////////////////////////////////////////////
void CrySimpleThreadSelf::WaitForThread()
{
	assert(m_thread);
	if( GetCurrentThreadId() != m_threadId )
	{
		WaitForSingleObject( (HANDLE)m_thread, INFINITE );
	}
}

CrySimpleThreadSelf::~CrySimpleThreadSelf()
{
	if(m_thread)
		CloseHandle(m_thread);
}

void CrySimpleThreadSelf::StartThread(unsigned (__stdcall *func)(void*), void* argList)
{
#ifdef DURANGO
	m_thread = (void*)CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)func, argList, CREATE_SUSPENDED, (LPDWORD)&m_threadId );
#else
	m_thread = (void*)_beginthreadex( NULL, 0, func, argList, CREATE_SUSPENDED, &m_threadId );
#endif
	assert(m_thread);
	ResumeThread((HANDLE)m_thread);
}


//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
void CryInterlockedPushEntrySList( SLockFreeSingleLinkedListHeader& list,  SLockFreeSingleLinkedListEntry &element )
{
	STATIC_CHECK(sizeof(SLockFreeSingleLinkedListHeader) == sizeof(SLIST_HEADER), CRY_INTERLOCKED_SLIST_HEADER_HAS_WRONG_SIZE);
	STATIC_CHECK(sizeof(SLockFreeSingleLinkedListEntry) == sizeof(SLIST_ENTRY), CRY_INTERLOCKED_SLIST_ENTRY_HAS_WRONG_SIZE);		
#if defined(XENON)
	InterlockedPushEntrySListRelease( alias_cast<PSLIST_HEADER>(&list), alias_cast<PSLIST_ENTRY>(&element) );
#else
	assert( IsAligned(&list,MEMORY_ALLOCATION_ALIGNMENT) && "LockFree SingleLink List Header has wrong Alignment" );
	assert( IsAligned(&element, MEMORY_ALLOCATION_ALIGNMENT) && "LockFree SingleLink List Entry has wrong Alignment" );	
	InterlockedPushEntrySList( alias_cast<PSLIST_HEADER>(&list), alias_cast<PSLIST_ENTRY>(&element) );
#endif
}

//////////////////////////////////////////////////////////////////////////
void* CryInterlockedPopEntrySList(  SLockFreeSingleLinkedListHeader& list )
{	
	STATIC_CHECK(sizeof(SLockFreeSingleLinkedListHeader) == sizeof(SLIST_HEADER), CRY_INTERLOCKED_SLIST_HEADER_HAS_WRONG_SIZE);
#if defined(XENON)
	return reinterpret_cast<void*>(InterlockedPopEntrySListAcquire(alias_cast<PSLIST_HEADER>(&list)));	
#else	
	assert( IsAligned(&list,MEMORY_ALLOCATION_ALIGNMENT) && "LockFree SingleLink List Header has wrong Alignment" );
	return reinterpret_cast<void*>(InterlockedPopEntrySList(alias_cast<PSLIST_HEADER>(&list)));	
#endif
}

//////////////////////////////////////////////////////////////////////////
void CryInitializeSListHead(SLockFreeSingleLinkedListHeader& list)
{
#if !defined(XENON)
	assert( IsAligned(&list,MEMORY_ALLOCATION_ALIGNMENT) && "LockFree SingleLink List Header has wrong Alignment" );
#endif
	STATIC_CHECK(sizeof(SLockFreeSingleLinkedListHeader) == sizeof(SLIST_HEADER), CRY_INTERLOCKED_SLIST_HEADER_HAS_WRONG_SIZE);
	InitializeSListHead(alias_cast<PSLIST_HEADER>(&list));
}

//////////////////////////////////////////////////////////////////////////
void* CryInterlockedFlushSList(SLockFreeSingleLinkedListHeader& list)
{
#if !defined(XENON)
	assert( IsAligned(&list,MEMORY_ALLOCATION_ALIGNMENT) && "LockFree SingleLink List Header has wrong Alignment" );
#endif
	STATIC_CHECK(sizeof(SLockFreeSingleLinkedListHeader) == sizeof(SLIST_HEADER), CRY_INTERLOCKED_SLIST_HEADER_HAS_WRONG_SIZE);
	return InterlockedFlushSList(alias_cast<PSLIST_HEADER>(&list));	
}

///////////////////////////////////////////////////////////////////////////////
// base class for lock less Producer/Consumer queue, due platforms specific they
// are implemeted in CryThead_platform.h
namespace CryMT {
namespace detail {

	///////////////////////////////////////////////////////////////////////////////
	void SingleProducerSingleConsumerQueueBase::Push( void *pObj, volatile uint32 &rProducerIndex, volatile uint32 &rComsumerIndex, uint32 nBufferSize, void *arrBuffer, uint32 nObjectSize )
	{
		// spin if queue is full
		int iter = 0;
		while(rProducerIndex - rComsumerIndex == nBufferSize) 
		{ 
			CryLowLatencySleep(iter++ > 10 ? 1 : 0); 
		}

		MemoryBarrier();
		char *pBuffer = alias_cast<char*>(arrBuffer);
		uint32 nIndex = rProducerIndex % nBufferSize;

		memcpy( pBuffer + (nIndex * nObjectSize), pObj, nObjectSize );
		MemoryBarrier();
		rProducerIndex += 1;
		MemoryBarrier();
	}

	///////////////////////////////////////////////////////////////////////////////
	void SingleProducerSingleConsumerQueueBase::Pop( void *pObj, volatile uint32 &rProducerIndex, volatile uint32 &rComsumerIndex, uint32 nBufferSize, void *arrBuffer, uint32 nObjectSize )
	{
		MemoryBarrier();
		// busy-loop if queue is empty
		int iter = 0;
		while(rProducerIndex - rComsumerIndex == 0) { CryLowLatencySleep(iter++ > 10 ? 1 : 0); }		

		char *pBuffer = alias_cast<char*>(arrBuffer);
		uint32 nIndex = rComsumerIndex % nBufferSize;

		memcpy( pObj, pBuffer + (nIndex * nObjectSize), nObjectSize );		
		MemoryBarrier();
		rComsumerIndex += 1;
		MemoryBarrier();

	}

	///////////////////////////////////////////////////////////////////////////////
	void N_ProducerSingleConsumerQueueBase::Push( void *pObj, volatile uint32 &rProducerIndex, volatile uint32 &rComsumerIndex, volatile uint32 &rRunning, void *arrBuffer, uint32 nBufferSize, uint32 nObjectSize, volatile uint32*	arrStates )
	{
		MemoryBarrier();
		uint32 nProducerIndex;
		uint32 nComsumerIndex;

		int iter = 0;
		do
		{
			nProducerIndex = rProducerIndex;
			nComsumerIndex = rComsumerIndex;
			
			if( nProducerIndex - nComsumerIndex == nBufferSize)
			{
				CryLowLatencySleep(iter++ > 10 ? 1 : 0); 
				if(iter > 20 ) // 10 spins + 10 ms wait
				{
					uint32 nSizeToAlloc = sizeof(SFallbackList) + nObjectSize - 1;
					SFallbackList *pFallbackEntry = (SFallbackList *)CryModuleMemalign(nSizeToAlloc,128);
					memcpy(pFallbackEntry->object, pObj, nObjectSize);
					MemoryBarrier();
					CryInterlockedPushEntrySList( fallbackList,  pFallbackEntry->nextEntry );
					return;
				}
				continue;
			}

			if(CryInterlockedCompareExchange( alias_cast<volatile LONG*>(&rProducerIndex), nProducerIndex + 1, nProducerIndex ) == nProducerIndex )
				break;
		}while(true);		

		MemoryBarrier();
		char *pBuffer = alias_cast<char*>(arrBuffer);
		uint32 nIndex = nProducerIndex % nBufferSize;

		memcpy( pBuffer + (nIndex * nObjectSize), pObj, nObjectSize);
		MemoryBarrier();
		arrStates[nIndex] = 1;
		MemoryBarrier();
	}

	///////////////////////////////////////////////////////////////////////////////
	bool N_ProducerSingleConsumerQueueBase::Pop( void *pObj, volatile uint32 &rProducerIndex, volatile uint32 &rComsumerIndex, volatile uint32 &rRunning, void *arrBuffer, uint32 nBufferSize, uint32 nObjectSize, volatile uint32 *arrStates )
	{
		MemoryBarrier();

		// busy-loop if queue is empty
		int iter = 0;
		do
		{ 
			SFallbackList *pFallback = (SFallbackList *)CryInterlockedPopEntrySList(fallbackList);
			IF(pFallback ,0)
			{
				memcpy( pObj, pFallback->object, nObjectSize );	
				CryModuleMemalignFree(pFallback);
				return true;
			}

			// on 360 avoid sleep as it can over run causing unnecessary idle
			// Instead just spin (this is only executed on the MT)
#if defined(XENON)
			__SetHWThreadPriorityLow();

			YieldProcessor();
			YieldProcessor();
			YieldProcessor();
			YieldProcessor();
			YieldProcessor();
			YieldProcessor();
			YieldProcessor();
			YieldProcessor();

			SwitchToThread();
			
			__SetHWThreadPriorityMed();
#else
			if(iter>10)
				Sleep(iter > 100 ? 1 : 0); 

			iter++;
#endif
		}while(rRunning && rProducerIndex - rComsumerIndex == 0);

		if( rRunning == 0 && rProducerIndex - rComsumerIndex == 0)
		{
			SFallbackList *pFallback = (SFallbackList *)CryInterlockedPopEntrySList(fallbackList);
			IF(pFallback ,0)
			{
				memcpy( pObj, pFallback->object, nObjectSize );	
				CryModuleMemalignFree(pFallback);
				return true;
			}
			// if the queue was empty, make sure we really are empty
			return false;
		}
			
		iter = 0;
		while(arrStates[rComsumerIndex % nBufferSize] == 0 )  { CryLowLatencySleep(iter++ > 10 ? 1 : 0); }
		
		char *pBuffer = alias_cast<char*>(arrBuffer);
		uint32 nIndex = rComsumerIndex % nBufferSize;

		memcpy(pObj, pBuffer + (nIndex * nObjectSize), nObjectSize);		
		MemoryBarrier();
		arrStates[nIndex] = 0;
		MemoryBarrier();
		rComsumerIndex += 1;
		MemoryBarrier();

		return true;
	}

} // namespace detail
} // namespace CryMT

#endif //__CRYTHREADIMPL_WINDOWS_H__
